﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Configuration;
using System.Reflection;
using System.Web;
using System.Web.Configuration;
using System.Web.Hosting;
using EPiServer.Configuration;
using EPiServer.Framework;
using EPiServer.Shell.Configuration;
using log4net;

namespace TemplateFoundation.Gadgets
{
    [InitializableModule]
    public class GadgetsInitializableModule : IInitializableModule
    {
        private readonly ILog _logger = LogManager.GetLogger(typeof (GadgetsInitializableModule));
        private bool _initialized;
        private const string GADGETS_MODULE_NAME = "EmbeddedGadgets"; // The value used for the "name" attribute used for the <add> element under <publicModules> in web.config

        #region IInitializableModule Members
        
        public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context)
        {
            try
            {
                if(_initialized)
                {
                    return;
                }

                // Find the <episerver.shell> element in web.config
                var shellSection = (EPiServerShellSection)ConfigurationManager.GetSection("episerver.shell");

                if(shellSection==null)
                {
                    _logger.Warn("There is no <episerver.shell> element in web.config, one-assembly gadgets will not be loaded");

                    _initialized = true;

                    return;
                }

                _logger.Debug("Initializing single-assembly gadgets");

                // Used for list of gadget assemblies
                var gadgetAssemblyNames = new List<string>();

                // Prefix which will be used to identify incoming resource requests
                var vppPathPrefix = string.Concat(VirtualPathUtility.AppendTrailingSlash(shellSection.PublicModules.RootPath), GADGETS_MODULE_NAME);

                // Find the gadget modules declaration in .webconfig
                foreach (ModuleElement module in shellSection.PublicModules)
                {
                    if (module.Name == GADGETS_MODULE_NAME)
                    {
                        // Add list of gadget assembly names
                        gadgetAssemblyNames.AddRange(module.Assemblies
                                                     .Cast<AssemblyInfo>()
                                                     .Select(assembly => assembly.Assembly));
                    }
                }

                // Add one virtual path provider for each gadget assembly
                foreach (var gadgetAssemblyName in gadgetAssemblyNames)
                {
                    _logger.DebugFormat("Registering virtual path provider for gadgets assembly '{0}' in site '{1}'",
                                  gadgetAssemblyName,
                                  Settings.Instance.SiteDisplayName);

                    // Load the gadget assembly
                    var assembly = Assembly.Load(gadgetAssemblyName);

                    if(assembly == null)
                    {
                        _logger.ErrorFormat("Failed to load gadgets assembly '{0}'", gadgetAssemblyName);

                        _initialized = true;

                        return;
                    }

                    // Add the virtual path provider
                    HostingEnvironment.RegisterVirtualPathProvider(new GadgetVirtualPathProvider(vppPathPrefix, assembly));
                }
            }
            catch(Exception ex)
            {
                _logger.ErrorFormat("Failed to initialize gadgets assembly virtual path provider: " + ex.Message);
            }

            _initialized = true;
        }

        public void Preload(string[] parameters)
        {
            throw new NotImplementedException();
        }

        public void Uninitialize(global::EPiServer.Framework.Initialization.InitializationEngine context)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}